//@author A0097978X
package flexiCommands;

import storage.TimeRange;
import storage.Time;
import storage.DateRange;
import storage.Date;

import java.lang.StringBuilder;
import java.lang.Integer;
import java.util.Calendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ParseEditInfo extends ParseInfo{
	
	private static final String ERROR_MESSAGE_TOO_FEW_ARGUMENT = "Too little info";
	private static final String ERROR_MESSAGE_OPTION_0 = "Please re-enter your option";
	private static final String ERROR_MESSAGE_INVALID_MONTH = "Month is invalid";
	private static final String ERROR_MESSAGE_INVALID_INPUT = "Invalid input, please try again!";
	private static final String ERROR_MESSAGE_RELATIVE_TIME_NOT_ALLOWED = "Relative time not allowed for Start Time";
	private static final String ERROR_MESSAGE_NO_VALUES_GIVEN = "Please state your values properly!";

	private static final String BY_SPACE_DASH = "(\\s)|[-]";
	private static final String BY_FIELD_TYPES = "(?i)(description|start|end)";
	private static final String BY_NUMBER_SYMBOL_WORD = "((\\d)+)((\\s)+)|((\\D)+)|((\\d)+)([/]*)([-]*)";
	private static final String REGEX_TIME = "((\\d?\\d)+((am|pm)|(:?\\d\\d)?(am|pm)?))+";

	private static final int QUERY_TYPE = 2;
	private static final int OPTION_TYPE = 1;
	private static final int YEAR_TYPE = 100;
	private static final int MONTH_TYPE = 31;
	private static final int OPTION = 1;
	private static final int COMMAND = 0;
	private static final int DESCRIPTION = 3;

	private static final int OPTION_SLOT = 1;
	private static final int PROCESSED = 3;
	private static final int UNPROCESSED = 0;
	private static final int NEXT_WORD = 1;
	private static final int FOLLOWING_WORD = 2;
	private static final int DATE_MONTH_YEAR_COMPLETED = 3;
	private static final int OCT_NOV_DEC = 10;
	private static final int INVALID_MONTH = 0;
	private static final int VALID_FINAL_DATE = 8;
	private static final int SINGLE_DIGIT = 1;
	private static final int DOUBLE_DIGIT = 2;
	private static final int TRIPLE_DIGIT = 3;
	private static final int HRS_1200 = 1200;
	private static final int AM_12 = 0;
	private static final int PM_12 = 12;
	private static final int TWO_ARGUMENTS = 2;
	private static final int NEXT_DAY = 1;
	private static final int NEXT_MONTH = 1;
	private static final int NEXT_WEEK = 7;
	private static final int NEXT_YEAR = 1;
	private static final int TWO_WORDS = 2;
	private static final int CURRENT = 0;
	private static final int JUST_BEFORE_1PM = 1259;
	private static final int JUST_AFTER_12PM = 1159;
	private static final int INVALID_SECOND = 59;
	private static final int INVALID_MINUTE = 59;
	private static final int INVALID_HR = 23;
	private static final int INVALID_YEAR = 2999;
	
	private static final String COLON = ":";
	private static final String DASH = "-";
	private static final String SLASH = "/";
	private static final String BLANK_SPACE = "";
	private static final String ONE_SPACE = " ";
	private static final String AM = "am";
	private static final String PM = "pm";
	private static final String DIGIT_20 = "20";
	private static final String YEAR_2013 = "2013";
	private static final String YEAR_2014 = "2014";
	private static final String DIGIT_0 = "0";
	private static final String DIGIT_00 = "00";
	private static final String DIGIT_0000 = "0000";
	private static final String ND_POSTFIX = "nd";
	private static final String ST_POSTFIX = "st";
	private static final String TH_POSTFIX = "th";
	private static final String IN_STRING = "IN";
	private static final String DATE_STRING = "DATE";
	private static final String TIME_STRING = "TIME";
	private static final String MONTH_TYPES = "MONTH";
	private static final String THIS_STRING = "THIS";
	private static final String NEXT_STRING = "NEXT";
	private static final String DAY_TYPES = "DAY";
	private static final String MIDNIGHT_STRING = "MIDNIGHT";
	private static final String NOON_STRING = "NOON";
	private static final String YEAR_TYPES = "(YEAR|YR)";
	private static final String WEEK_TYPES = "(WEEK|WK)";
	private static final String TOMORROW_TYPES = "(TOMORROW|TMW|TMR|TML)";
	private static final String TODAY_TYPES = "(TODAY|TDY)";
	private static final String SUNDAY_TYPES = "(SUNDAY|SUN)";
	private static final String SATURDAY_TYPES = "(SATURDAY|SAT}";
	private static final String FRIDAY_TYPES = "(FRIDAY|FRI)";
	private static final String THURSDAY_TYPES = "(THURSDAY|THURS|THUR)";
	private static final String WEDNESDAY_TYPES = "(WEDNESDAY|WEDS|WED)";
	private static final String TUESDAY_TYPES = "(TUESDAY|TUES|TUE)";
	private static final String MONDAY_TYPES = "(MONDAY|MON)";
	private static final String HOURS_TYPES = "(HOURS|HOUR|HRS|HR)";
	private static final String MINUTES_TYPES = "(MINUTES|MINUTE|MINS|MIN)";
	private static final String RELATIVE_TYPE = "(THIS|NEXT|TOMORROW|TMW|TMR|TML|TODAY|TDY|NOON|MIDNIGHT)";
	private static final Object AUGUST_STRING = "AUGUST";

	public enum FIELDS_TYPE { DESCRIPTION, START_TIME, END_TIME, START_DATE, END_DATE, INVALID };

	private String[] infoToParse;
	private Command processedCmd;
	private int[] typeDeterminer;

	private Pattern stringPattern;
	private Matcher stringMatcher;

	public ParseEditInfo(String[] userInfo) throws Exception{
		super(userInfo);

		if(userInfo.length < TWO_ARGUMENTS)
			throw new Exception(ERROR_MESSAGE_TOO_FEW_ARGUMENT);
	}

	@Override
	public Command execute() throws NumberFormatException, Exception {
		infoToParse = super.infoString;
		processedCmd = super.cmdObj;

		processedCmd.setCommandType(infoToParse[COMMAND]);

		typeDeterminer = new int[infoToParse.length];

//======================================================================//
//====================== Flexible Arguments Type =======================//
//======================================================================//

		for(int i=1; i<infoToParse.length; i++) {
			if(typeDeterminer[i] == UNPROCESSED){

				if(i == OPTION_SLOT && isInteger(infoToParse[i])) 
					typeDeterminer[i] = OPTION_TYPE;
				else if(i == OPTION_SLOT && !isInteger(infoToParse[i])) 
					throw new Exception(ERROR_MESSAGE_OPTION_0);	

				if(i != OPTION_SLOT)
					typeDeterminer[i] = QUERY_TYPE;
			}
		}

	//==== Parse & Generate Respective Type ====//

		if(checkTypeDeterminer()) {
			for(int q=1; q<typeDeterminer.length; q++) {
				if(typeDeterminer[q] == QUERY_TYPE) {
					generateQuery();
					break;
				}
			}												

			generateOption();	}
		else {
			throw new Exception(ERROR_MESSAGE_INVALID_INPUT);
		}

		setFieldsFromQuery();

		return processedCmd;
	}

//======================================================================//
//======================================================================//

	private boolean checkTypeDeterminer() {
		for(int i=1; i<typeDeterminer.length; i++) {
			if(typeDeterminer[i] == UNPROCESSED)
				return false;
		}

		return true;
	}

	private void generateQuery() {
		StringBuilder query = new StringBuilder();
		for(int i=1; i<typeDeterminer.length; i++) {
			if(typeDeterminer[i] == QUERY_TYPE) {
				query.append(infoToParse[i] + ONE_SPACE);
			}
		}

		processedCmd.setQuery(query.toString().trim());
	}

	private void setFieldsFromQuery() throws NumberFormatException, Exception {
		String[] queries = processedCmd.getQuery().split(BY_SPACE_DASH);
		Time changeTime;
		Date changeDate;

		int processThis = 2;
		for(int i=0; i<queries.length; i++, processThis++) {
			if(typeDeterminer[processThis] != PROCESSED) {
				switch(determineFieldType(queries, i)) {
					case DESCRIPTION:
						if((i+FOLLOWING_WORD)>queries.length-1) throw new Exception(ERROR_MESSAGE_NO_VALUES_GIVEN);
						
						processedCmd.setDescription(getDescription(queries, i+NEXT_WORD, processThis));
						break;

					case START_TIME:
						if((i+FOLLOWING_WORD)>queries.length-1) throw new Exception(ERROR_MESSAGE_NO_VALUES_GIVEN);
						
						if(queries[i+FOLLOWING_WORD].toUpperCase().matches(IN_STRING))
							throw new Exception(ERROR_MESSAGE_RELATIVE_TIME_NOT_ALLOWED);
						else
							changeTime = getTime(queries, i+FOLLOWING_WORD, processThis);

						if(!processedCmd.isEndTimeEmpty()) 
							processedCmd.setTimeRange(new TimeRange(changeTime, processedCmd.getTimeRange().getEndTime())); 
						else 
							processedCmd.setTimeRange(new TimeRange(changeTime, new Time(INVALID_HR, INVALID_MINUTE, INVALID_SECOND))); 
						break;

					case END_TIME:
						if((i+FOLLOWING_WORD)>queries.length-1) throw new Exception(ERROR_MESSAGE_NO_VALUES_GIVEN);
						
						if(queries[i+FOLLOWING_WORD].toUpperCase().matches(IN_STRING))
							changeTime = getRelativeTime(queries, i+FOLLOWING_WORD, processThis);
						else
							changeTime = getTime(queries, i+FOLLOWING_WORD, processThis);
						
						if(!processedCmd.isStartTimeEmpty()) 
							processedCmd.setTimeRange(new TimeRange(processedCmd.getTimeRange().getStartTime(), changeTime)); 
						else 
							processedCmd.setTimeRange(new TimeRange(new Time(), changeTime)); 
						break;

					case START_DATE:
						if((i+FOLLOWING_WORD)>queries.length-1) throw new Exception(ERROR_MESSAGE_NO_VALUES_GIVEN);
						
						if(queries[i+FOLLOWING_WORD].toUpperCase().matches(RELATIVE_TYPE))
							changeDate = getRelativeDate(queries, i+FOLLOWING_WORD, processThis);
						else
							changeDate = getDate(queries, i+FOLLOWING_WORD, processThis);

						if(!processedCmd.isEndDateEmpty())
							processedCmd.setDateRange(new DateRange(changeDate, processedCmd.getDateRange().getEndDate()));
						else
							processedCmd.setDateRange(new DateRange(changeDate, new Date(INVALID_YEAR)));
						break;

					case END_DATE:
						if((i+FOLLOWING_WORD)>queries.length-1) throw new Exception(ERROR_MESSAGE_NO_VALUES_GIVEN);
						
						if(queries[i+FOLLOWING_WORD].toUpperCase().matches(RELATIVE_TYPE)) 
							changeDate = getRelativeDate(queries, i+FOLLOWING_WORD, processThis);
						else 
							changeDate = getDate(queries, i+FOLLOWING_WORD, processThis); 

						if(!processedCmd.isStartDateEmpty())
							processedCmd.setDateRange(new DateRange(processedCmd.getDateRange().getStartDate(), changeDate));
						else
							processedCmd.setDateRange(new DateRange(new Date(), changeDate));
						break;				

					default:
						if(typeDeterminer[i] != PROCESSED) {
							processedCmd.setDescription(getDescription(queries, i, processThis));	}
						break;
				}
			}

		}
	}

	private FIELDS_TYPE determineFieldType(String[] queries, int idx) {
		switch(queries[idx].toUpperCase()) {
			
			case "DESCRIPTION":
				return FIELDS_TYPE.DESCRIPTION;

			case "START":
				if(queries[idx+NEXT_WORD].toUpperCase().equals(TIME_STRING))
					return FIELDS_TYPE.START_TIME;
				else if(queries[idx+NEXT_WORD].toUpperCase().equals(DATE_STRING))
					return FIELDS_TYPE.START_DATE;
				else 
					return FIELDS_TYPE.DESCRIPTION;		//user type: Start Party

			case "END":
				if(queries[idx+NEXT_WORD].toUpperCase().equals(TIME_STRING))
					return FIELDS_TYPE.END_TIME;
				else if(queries[idx+NEXT_WORD].toUpperCase().equals(DATE_STRING))
					return FIELDS_TYPE.END_DATE;
				else 
					return FIELDS_TYPE.DESCRIPTION;		//user type: End Road

			default:									//by default edit & replace description field
				return FIELDS_TYPE.INVALID;
		}
	}

//===================================================================================//
//============================ Methods for Parsing Fields ===========================//
//===================================================================================//

	private void generateOption() throws Exception{
		processedCmd.setOption(Integer.parseInt(infoToParse[OPTION]));
	}

	private String getDescription(String[] queries, int idx, int processThis) {
		StringBuilder changeDescription = new StringBuilder();
		stringPattern = Pattern.compile(BY_FIELD_TYPES);

		for(int i=idx; i<queries.length; i++, processThis++) {
			stringMatcher = stringPattern.matcher(queries[i]);

			if(!stringMatcher.matches()) {
				changeDescription.append(queries[i] + ONE_SPACE);
				typeDeterminer[processThis] = DESCRIPTION;	}
			else
				break;
		}

		return changeDescription.toString().trim();
	}

	private Time getRelativeTime(String[] queries, int idx, int processThis) throws NumberFormatException, Exception {
		StringBuilder changeRelativeTime = new StringBuilder();
		stringPattern = Pattern.compile(BY_FIELD_TYPES);
		
		typeDeterminer[processThis] = typeDeterminer[processThis+NEXT_WORD] = PROCESSED;
		processThis += FOLLOWING_WORD;

		for(int i=idx; i<queries.length; i++, processThis++){
			stringMatcher = stringPattern.matcher(queries[i]);

			if(!stringMatcher.matches()) {
				changeRelativeTime.append(queries[i] + ONE_SPACE);
				typeDeterminer[processThis] = PROCESSED; }
			else
				break;
		}

		String[] timeParts = changeRelativeTime.toString().split(BY_SPACE_DASH);

		Calendar cal = Calendar.getInstance();
		Time firstTime = new Time();
		
		if(timeParts.length > TWO_WORDS) {
			if(timeParts[FOLLOWING_WORD].toUpperCase().matches(MINUTES_TYPES))
				cal.add(Calendar.MINUTE, Integer.parseInt(timeParts[NEXT_WORD]));
	    
			else if(timeParts[FOLLOWING_WORD].toUpperCase().matches(HOURS_TYPES)) 
				cal.add(Calendar.HOUR_OF_DAY, Integer.parseInt(timeParts[NEXT_WORD]));		}

		else {
	    	if(timeParts[CURRENT].toUpperCase().equals(NOON_STRING)) {
	    		cal.set(Calendar.HOUR_OF_DAY, PM_12);
	    		cal.set(Calendar.MINUTE, 0);			}
	    
	   		else if(timeParts[CURRENT].toUpperCase().equals(MIDNIGHT_STRING)) {
	    		cal.set(Calendar.HOUR_OF_DAY, AM_12);
	    		cal.set(Calendar.MINUTE, 0);			}
	   	}

	    firstTime.setHour(cal.get(Calendar.HOUR_OF_DAY));
	    firstTime.setMinute(cal.get(Calendar.MINUTE));

		return firstTime;
	}

	private Date getRelativeDate(String[] queries, int idx, int processThis) throws NumberFormatException, Exception {
		StringBuilder changeRelativeDate = new StringBuilder();
		stringPattern = Pattern.compile(BY_FIELD_TYPES);
		
		typeDeterminer[processThis] = typeDeterminer[processThis+NEXT_WORD] = PROCESSED;
		processThis += FOLLOWING_WORD;

		for(int i=idx; i<queries.length; i++, processThis++){
			stringMatcher = stringPattern.matcher(queries[i]);

			if(!stringMatcher.matches()) {
				changeRelativeDate.append(queries[i] + ONE_SPACE);
				typeDeterminer[processThis] = PROCESSED; }
			else
				break;
		}

		String[] dayParts = changeRelativeDate.toString().split(BY_SPACE_DASH);

		Calendar cal = Calendar.getInstance();
		Date firstDate = new Date();
		
		int day = cal.get(Calendar.DAY_OF_MONTH);
	    int month = cal.get(Calendar.MONTH) + 1;				
	    

	    int count = 0;
	    while(count < dayParts.length) {
	    	if(dayParts[count].toUpperCase().matches(TODAY_TYPES)) { } //do nothing
		
	    	if(dayParts[count].toUpperCase().matches(TOMORROW_TYPES)) {
	    		cal.add(Calendar.DATE, NEXT_DAY);

	    		if(cal.get(Calendar.DAY_OF_MONTH) < day && cal.get(Calendar.MONTH) > month) 		cal.add(Calendar.MONTH, NEXT_MONTH);
	    	}
		
	    	if(dayParts[count].toUpperCase().equals(THIS_STRING) && count < dayParts.length-1) {
	    			 if(dayParts[count+NEXT_WORD].toUpperCase().matches(MONDAY_TYPES)) 				cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(TUESDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(WEDNESDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(THURSDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(FRIDAY_TYPES)) 				cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SATURDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SUNDAY_TYPES)) 				cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
	    		
	    		count++;
	    	}
		
	    	if(dayParts[count].toUpperCase().equals(NEXT_STRING) && count < dayParts.length-1) {
	    			 if(dayParts[count+NEXT_WORD].toUpperCase().matches(MONTH_TYPES)) 				cal.add(Calendar.MONTH, NEXT_MONTH);		
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(WEEK_TYPES)) 				cal.add(Calendar.DATE, NEXT_WEEK);		
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(YEAR_TYPES)) 				cal.add(Calendar.YEAR, NEXT_YEAR);		
	    		else if(dayParts[count+NEXT_WORD].toUpperCase().matches(DAY_TYPES))					cal.add(Calendar.DATE, NEXT_DAY);	

	    		else {
	    				 if(dayParts[count+NEXT_WORD].toUpperCase().matches(MONDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(TUESDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(WEDNESDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(THURSDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(FRIDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SATURDAY_TYPES)) 		cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
	    			else if(dayParts[count+NEXT_WORD].toUpperCase().matches(SUNDAY_TYPES)) 			cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
			
	    			//next week
	    			cal.add(Calendar.DATE, NEXT_WEEK);		}

	    		count++;	
	    	}
	    	
	    	if(cal.get(Calendar.DAY_OF_MONTH) < day && cal.get(Calendar.MONTH) > month)				cal.add(Calendar.MONTH, NEXT_MONTH);
	    	
	    	firstDate.setDay(cal.get(Calendar.DAY_OF_MONTH));
	    	firstDate.setMonth(cal.get(Calendar.MONTH) + 1);
	    	firstDate.setYear(cal.get(Calendar.YEAR));
	    	
	    	count++;
	    }

		 return firstDate; 
	}

	private Date getDate(String[] queries, int idx, int processThis) throws NumberFormatException, Exception {
		StringBuilder changeDate = new StringBuilder();
		stringPattern = Pattern.compile(BY_FIELD_TYPES);

		typeDeterminer[processThis] = typeDeterminer[processThis+NEXT_WORD] = PROCESSED;
		processThis += FOLLOWING_WORD;

		for(int i=idx; i<queries.length; i++, processThis++){
			stringMatcher = stringPattern.matcher(queries[i]);

			if(!stringMatcher.matches()) {
				changeDate.append(queries[i] + ONE_SPACE);
				typeDeterminer[processThis] = PROCESSED; }
			else
				break;
		}

		StringBuilder changeDate_newform = new StringBuilder();
		stringPattern = Pattern.compile(BY_NUMBER_SYMBOL_WORD, Pattern.CASE_INSENSITIVE);
		stringMatcher = stringPattern.matcher(changeDate.toString());

		int date_count = 1;
		while(stringMatcher.find()) {
			String current = stringMatcher.group().trim();

			if(current.contains(SLASH) || current.contains(DASH)) {
				current = current.substring(0, current.length()-1); }

			if((current.contains(TH_POSTFIX) || current.contains(ST_POSTFIX) || current.contains(ND_POSTFIX)) && !current.toUpperCase().equals(AUGUST_STRING)) {
				current = current.substring(3, current.length()); }

	//==== Integer (Numerical Digits) ====//

			if(isInteger(current) && (Integer.parseInt(current) < MONTH_TYPE) && (date_count<DATE_MONTH_YEAR_COMPLETED)) {	
		 		date_count++;			
				if(Integer.parseInt(current) < 10) {
					if(current.length() > 1) changeDate_newform.append(current);		//ie. 01
					else changeDate_newform.append(DIGIT_0 + current);} 				//ie. _1
				else changeDate_newform.append(current); 	}

		    else if (date_count == DATE_MONTH_YEAR_COMPLETED) {
				if(Integer.parseInt(current) < YEAR_TYPE)
					changeDate_newform.append(DIGIT_20 + current);
				else 
					changeDate_newform.append(current); 	    
				date_count++;	}

	//==== Characters (Alphabets) ====//

			else {
		    	int month = getMonth(current);
		    	if(month < OCT_NOV_DEC && month != INVALID_MONTH) {
		    		date_count++;
		    		changeDate_newform.append(DIGIT_0 + month);	}
		    	else {
		    		if(month != INVALID_MONTH) {
		    			date_count++;
		    			changeDate_newform.append(month);}	
		    		else
		    			throw new Exception(ERROR_MESSAGE_INVALID_MONTH);	}
		    }

		}
	
	//==== Year Determiner ====//

		Calendar cal = Calendar.getInstance();
	    int month = cal.get(Calendar.MONTH) + 1;     
	    int day = cal.get(Calendar.DAY_OF_MONTH);

	    if(date_count <= DATE_MONTH_YEAR_COMPLETED && changeDate_newform.toString().length() < VALID_FINAL_DATE) {
			if(Integer.parseInt(changeDate_newform.toString().substring(2,4)) < (month)) {
				changeDate_newform.append(YEAR_2014); }
			else if(Integer.parseInt(changeDate_newform.toString().substring(2,4)) == (month)) {

				//Same Month as Current Month

				if(Integer.parseInt(changeDate_newform.toString().substring(0,2)) < day)
					changeDate_newform.append(YEAR_2014);
				else
					changeDate_newform.append(YEAR_2013);		}

			else
				changeDate_newform.append(YEAR_2013);	}

		Date newDate = new Date();
		newDate.setDay(Integer.parseInt(changeDate_newform.toString().substring(0,2)));
		newDate.setMonth(Integer.parseInt(changeDate_newform.toString().substring(2,4)));
		newDate.setYear(Integer.parseInt(changeDate_newform.toString().substring(4,8)));

		return newDate;
	}

	private Time getTime(String[] queries, int idx, int processThis) throws NumberFormatException, Exception {
		StringBuilder changeTime = new StringBuilder();
		stringPattern = Pattern.compile(BY_FIELD_TYPES);

		typeDeterminer[processThis] = typeDeterminer[processThis+NEXT_WORD] = PROCESSED;				
		processThis += FOLLOWING_WORD;

		for(int i=idx; i<queries.length; i++, processThis++){
			stringMatcher = stringPattern.matcher(queries[i]);

			if(!stringMatcher.matches()) {
				changeTime.append(queries[i] + ONE_SPACE);
				typeDeterminer[processThis] = PROCESSED; }
			else
				break;
		}

		StringBuilder changeTime_newform = new StringBuilder();
		stringPattern = Pattern.compile(REGEX_TIME, Pattern.CASE_INSENSITIVE);
		stringMatcher = stringPattern.matcher(changeTime.toString());

		while(stringMatcher.find()) {
			String current = stringMatcher.group().trim();
			int time = 0;

			if(current.contains(PM)) {
				current = current.replace(PM, BLANK_SPACE);

				if(current.length() == SINGLE_DIGIT) {
					time = Integer.parseInt(current) + PM_12;
					changeTime_newform.append(time + DIGIT_00); }
				else if (current.length() == DOUBLE_DIGIT) {
					if(Integer.parseInt(current) < PM_12) {
						time = Integer.parseInt(current) + PM_12;
						changeTime_newform.append(time + DIGIT_00);	}
					else
						changeTime_newform.append(current + DIGIT_00);	 }
				else if (current.contains(COLON)) {
					current = current.replace(COLON, BLANK_SPACE);
					if(Integer.parseInt(current) < HRS_1200 ) {
						time = Integer.parseInt(current) + HRS_1200;
						changeTime_newform.append(time); } 
					else
						changeTime_newform.append(current);	}
																		}
			else if(current.contains(AM)) {
				current = current.replace(AM, BLANK_SPACE);

				if(current.length() == SINGLE_DIGIT)
					changeTime_newform.append(DIGIT_0 + current + DIGIT_00);
				else if (current.length() == DOUBLE_DIGIT) {
					if(Integer.parseInt(current) == PM_12)
						changeTime_newform.append(DIGIT_0000);
					else
						changeTime_newform.append(current + DIGIT_00); }
				else if (current.contains(COLON)) {
					current = current.replace(COLON, BLANK_SPACE);
					if(Integer.parseInt(current) > JUST_AFTER_12PM && Integer.parseInt(current) < JUST_BEFORE_1PM )
						changeTime_newform.append(DIGIT_00 + current.substring(2,4));
					else
						changeTime_newform.append(current);	} 
																		}
			else if(current.contains(COLON)) {
				current = current.replace(COLON, BLANK_SPACE);

				if(current.length() == TRIPLE_DIGIT)
					changeTime_newform.append(DIGIT_0 + current);
				else
					changeTime_newform.append(current);	
																		}
			else {
				if(current.length() == SINGLE_DIGIT)
					changeTime_newform.append(DIGIT_0 + current + DIGIT_00);
				else if(current.length() == DOUBLE_DIGIT)
					changeTime_newform.append(current + DIGIT_00);
				else if(current.length()==TRIPLE_DIGIT)
					changeTime_newform.append(DIGIT_0 + current);
				else
					changeTime_newform.append(current);			}

		}

		Time newTime = new Time();
		newTime.setHour(Integer.parseInt(changeTime_newform.toString().substring(0,2)));
		newTime.setMinute(Integer.parseInt(changeTime_newform.toString().substring(2,4)));

		return newTime;
	}

//==========================================================================//
//============================ Auxiliary Methods ===========================//
//==========================================================================//

	private boolean isInteger(String str) {
    	try {
        	Integer.parseInt(str);
        	return true;
    	} catch (NumberFormatException nfe) {
        	return false;
        }
    }

    private int getMonth(String currMonth) {
		switch(currMonth.toUpperCase()) {
			case "JAN": case "JANUARY":
				return 1;
			case "FEB": case "FEBRUARY":
				return 2;
			case "MAR": case "MARCH":
				return 3;
			case "APR": case "APRIL":
				return 4;
			case "MAY": 
				return 5;
			case "JUN": case "JUNE":
				return 6;
			case "JUL": case "JULY":
				return 7;
			case "AUG": case "AUGUST":
				return 8;
			case "SEP": case "SEPT": case "SEPTEMBER":
				return 9;
			case "OCT": case "OCTOBER":
				return 10;
			case "NOV": case "NOVEMBER":
				return 11;
			case "DEC": case "DECEMBER":
				return 12;
			default:
				return 0;
		}
	}
}